package cn.bonoon.kernel.io;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import cn.bonoon.kernel.util.MD5Util;
import cn.bonoon.util.PathUtil;

/**
 * 定义文件如何进行存储；
 * 这里需要存储的文件数量不定，可以解释所有的文件
 * 
 * @author jackson
 * @see StorageMultipartFile
 * @see StorageMultipartRequest
 */
public abstract class StorageFile extends AbstractBFile implements FileStorable{
	
	public static StorageMultipartFile valueOf(Object catalog, MultipartFile... files){
		return new StorageMultipartFile(catalog, files);
	}

	public static StorageMultipartFile valueOf(Object catalog, Collection<MultipartFile> files){
		return new StorageMultipartFile(catalog, files);
	}
	
	public static StorageMultipartRequest valueOf(Object catalog, MultipartHttpServletRequest multipartRequest, String...filenames){
		return new StorageMultipartRequest(catalog, multipartRequest, filenames);
	}

	public static StorageMultipartRequest valueOf(Object catalog, HttpServletRequest request, String...filenames){
		return new StorageMultipartRequest(catalog, request, filenames);
	}
	
	/** 可以支持路径为任何的类型 */
	private List<Object> folders = new ArrayList<>();
	
	/** 保存的路径的生成策略 */
	private DirectoryStrategy directoryStrategy = DirectoryStrategy.YEAR_MONTH_DAY;
	
	/** 用于保存的文件名的生成策略 */
	private FilenameStrategy filenameStrategy = FilenameStrategy.MD5;
	
	/** @see #setStorageHandler(FileStoreHandler) */
	private FileStoreHandler storageHandler;
	
	private String namePrefix = "", nameSuffix = "";
	
	/** 数据库的存储对象类型 */
	private Class<?> storageClassType;
	
	private boolean reversed;

	protected StorageFile(Object catalog){
		String ctem = "";
		
		if(catalog instanceof String){
			ctem = (String)catalog;
		}if(catalog instanceof Class){
			storageClassType = (Class<?>)catalog; 
			StorageCatalog sCatalog = storageClassType.getAnnotation(StorageCatalog.class);
			if(null != sCatalog){
				ctem = sCatalog.value();
				this.directoryStrategy = sCatalog.directoryStrategy();
				this.filenameStrategy  = sCatalog.filenameStrategy();
				this.reversed = sCatalog.reversed();
				Collections.addAll(this.folders, sCatalog.folders());
			}
		}
		
		this.catalog = ctem;
	}
	
	protected StorageFile(Object catalog, DirectoryStrategy directoryStrategy, FilenameStrategy filenameStrategy){
		this(catalog);
		if(null != directoryStrategy) this.directoryStrategy = directoryStrategy;
		if(null != filenameStrategy)  this.filenameStrategy = filenameStrategy;
	}
	
	public Class<?> getStorageClassType() {
		return storageClassType;
	}
	
	@SuppressWarnings("unchecked")
	public <T extends StoragePersistable> T instance(FileInfo info) throws Exception{
		Assert.notNull(storageClassType, "没有指定存储的类型！");
		
		return info.instance((T)storageClassType.newInstance());
	}
	
	/** 
	 * 在目录的后面可以再加子目录 ；
	 * 如果：
	 * catalog = "china"
	 * directoryStrategy = DirectoryStrategy.YEAR
	 * dir = "gg/dd"
	 * 则生成的路径会是
	 * "/root/china/2016/gg/dd"
	 */
	public void folders(Object... folders) {
		Collections.addAll(this.folders, folders);
	}
	
	@Override
	public abstract MultipartFile[] getSourceFile();
	
	StorageFile init(StorageInitizlier initizlier){
		String relativePath = directoryStrategy.formatPath();
		if(reversed){
			relativePath = PathUtil.path(folders, relativePath);
		}else{
			relativePath = PathUtil.path(relativePath, folders);
		}
		
		relativePath = initizlier.resolveEmbeddedValue(relativePath);
		this.sourcePath = PathUtil.endWithSeparator(relativePath);
		
		return this;
	}
	
	/** 解析文件名，并且如果判断到已经存在相同文件名的，则会重复调用多次，并且指定重复的数据"repeatTimes" */
	public String resolveFilename(MultipartFile sourceFile, int repeatTimes){
		String originalFilename = sourceFile.getOriginalFilename();
		int extp = originalFilename.lastIndexOf(".");
		if(extp >= 0){
			filesuffix = originalFilename.substring(extp);
			originalFilename = originalFilename.substring(0, extp);
		}else{
			filesuffix = "";
		}
		
		String name = "";
		switch (filenameStrategy) {
		case MD5:
			name = MD5Util.randomMD5String();
			break;
		case TIMESTAMP:
			if(repeatTimes > 0){
				try {//如果时间戳相同。。。那机率极小
					Thread.sleep(100);
				} catch (InterruptedException e) {
					//这里不需要做什么
					e.printStackTrace();
				}
			}
			name = Long.toString(System.currentTimeMillis());
			break;
		default:
			name = originalFilename;
			int lastp = name.lastIndexOf(PathUtil.separatorChar);
			
			if(lastp >= 0){
				name = name.substring(lastp + 1);
			}
			if(repeatTimes > 0){
				name += '(' + repeatTimes + ')';
			}
			break;
		}
		
		return (filename = namePrefix + name + nameSuffix + filesuffix);
	}
	
	/**  如果调用了{@link #assertFile}方法，则表示必须要有上传文件，并且解析的结果要与指定的结果相同 */
	public void assertFile(int count, StorageFileLimit type){
		MultipartFile[] files = getSourceFile();
		Assert.notEmpty(files, "必须上传文件(图片)！");
		if(count > 0){
			type.check(files, count);
		}
	}

	/** {@link StorageFileLimit#ONLY} */
	public void only(int count){
		assertFile(count, StorageFileLimit.ONLY);
	}
	
	/** {@link StorageFileLimit#MAX} */
	public void max(int count){
		assertFile(count, StorageFileLimit.MAX);
	}

	/** {@link StorageFileLimit#MIN} */
	public void min(int count){
		assertFile(count, StorageFileLimit.MIN);
	}
	
	/** 检查上传的文件是否为空 */
	public void assertFile(){
		assertFile(0, StorageFileLimit.ONLY);
	}
	
	public boolean hasFile(){
		for(MultipartFile file : getSourceFile()){
			if(!file.isEmpty()) return true;
		}
		return false;
	}

	/** 检查文件的后缀 */
	public void assertSuffix(String msg, String...acceptSuffixes){
		
		for(int i = 0; i < acceptSuffixes.length; i++){
			if(acceptSuffixes[i].charAt(0) != '.'){
				acceptSuffixes[i] = "." + acceptSuffixes[i].toLowerCase();
			}else{
				acceptSuffixes[i] = acceptSuffixes[i].toLowerCase();
			}
		}
		
		NEXT : for(MultipartFile file : getSourceFile()){
			if(null == file || file.isEmpty()) continue;
			String fname = file.getOriginalFilename().toLowerCase();
			for(String accept : acceptSuffixes){
				if(fname.endsWith(accept)) continue NEXT;
			}
			throw new RuntimeException(msg);
		}
	}

	/** 文件名的前缀 */
	public void setNamePrefix(String namePrefix) {
		this.namePrefix = namePrefix;
	}
	
	/** 文件名的后缀 */
	public void setNameSuffix(String nameSuffix) {
		this.nameSuffix = nameSuffix;
	}
	
	public void setFilenameStrategy(FilenameStrategy filenameStrategy) {
		this.filenameStrategy = filenameStrategy;
	}
	
	public void setDirectoryStrategy(DirectoryStrategy directoryStrategy) {
		this.directoryStrategy = directoryStrategy;
	}
	
	/** 支持在存储文件的时候，对文件进行某些必要的处理，如，打包、压缩等 */
	public void setStorageHandler(FileStoreHandler storageHandler) {
		this.storageHandler = storageHandler;
	}
	
	public FileStoreHandler getStorageHandler() {
		return storageHandler;
	}
	
	public void setReversed(boolean reversed) {
		this.reversed = reversed;
	}
}
